/**
 * Created by henian.xu on 2020/1/16.
 * 改用 Object.defineProperty 观察对象以兼容校低版本的浏览器
 * TODO 使用 Proxy 实现未设置 api 就尝试调用 mock
 */
import { assert, hasOwn, isFunction, isObject } from 'utils/index';

// 全局 Api 模块都会挂在这里
export const Api = {};

function firstLowerCase(str) {
  str += '';
  return str.replace(/\b(\w)|\s(\w)/g, (m) => m.toLowerCase());
}
function observe(obj, attach) {
  const target = { ...obj };
  Object.keys(target).forEach((key) => {
    // eslint-disable-next-line no-use-before-define
    const res = defineReactive(target, key);
    if (isObject(attach)) {
      assert(!hasOwn(attach, key), `${key} 模块已经存在了`, 'Api');
      // eslint-disable-next-line no-param-reassign
      attach[key] = res;
    }
  });
  return target;
}

function defineReactive(obj, key) {
  const property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) return null;

  let value = obj[key];
  if (isObject(value) || isFunction(value)) {
    if (isObject(value)) value = observe(value);
    Object.defineProperty(value, '__PARENT_PATH__', {
      get() {
        const __PARENT_PATH__ = [...(obj.__PARENT_PATH__ || [])];
        __PARENT_PATH__.push(firstLowerCase(key));
        return __PARENT_PATH__;
      },
    });
  }
  Object.defineProperty(obj, key, {
    get() {
      if (isFunction(value)) {
        const currentPath = value.__PARENT_PATH__;
        const callBack = value.bind({
          url: currentPath.join('/'),
          baseUrl: [...currentPath]
            .splice(0, Math.max(0, currentPath.length - 1))
            .join('/'),
          paths: [...currentPath],
        });
        return (...args) => callBack(...args);
      }
      return value;
    },
  });
  return value;
}

export function setApi(target) {
  observe(target, Api);
  return Api;
}
